/*!
\copyright  Copyright (c) 2022 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief   Implementations for the LE Unicast Manager message task.
*/

#if defined(INCLUDE_LE_AUDIO_UNICAST)

#include "le_unicast_manager_task.h"
#include "call_control_client.h"
#include "media_control_client.h"
#include "pacs_utilities.h"
#include "gatt_connect.h"
#include <timestamp_event.h>
#include <panic.h>
#include "gatt_tmas_server.h"
#include "tmap_profile.h"
#include "micp_server.h"
#include "le_unicast_music_source.h"
#include "telephony_messages.h"
#include "mirror_profile.h"
#include "kymera.h"
#include "kymera_le_mic_chain.h"

#include "le_unicast_music_source.h"

#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)
#include "device_list.h"
#include "device_properties.h"
#include "gatt.h"
#include "telephony_messages.h"
#include "av.h"
#include "audio_router.h"
#endif

/*! Offset of client configuration within the structure. */
#define leUnicastManager_GetCCCDOffset()  (offsetof(BapAscsConfig, aseControlPointCharClientCfg))
#define leUnicastManager_GetClientCfgOffset()  (offsetof(BapAscsConfig, aseCharClientCfg))

/*! Minimum GATT MTU needed to support LE unicast. */
#define leUnicastManagerConfig_GattMtuMinimum() 100

/*! Maximum number of active ASEs supported per Gatt connection. */
#define NUMBER_OF_ASES_REQUIRED    6

static void leUnicastManager_OnGattConnect(gatt_cid_t cid);
static void leUnicastManager_OnGattDisconnect(gatt_cid_t cid);
static void leUnicastManager_OnEncryptionChanged(gatt_cid_t cid, bool encrypted);

static const gatt_connect_observer_callback_t leUnicastManager_connect_callbacks =
{
    .OnConnection = leUnicastManager_OnGattConnect,
    .OnDisconnection = leUnicastManager_OnGattDisconnect,
    .OnEncryptionChanged = leUnicastManager_OnEncryptionChanged
};

static bool leUnicastManager_SampleRateLookupOnPacs(bool is_source, uint16 audiocontext, uint16 sample_rate, uint16 codec_type)
{
    bool match_found = FALSE;
    uint16 no_of_pacs;
    uint8 pacs_index;

    switch(codec_type)
    {
        case KYMERA_LE_AUDIO_CODEC_LC3:
        {
            const GattPacsServerRecordType* pacs_record = LeBapPacsUtilities_GetPacs(is_source, &no_of_pacs);
        
            for (pacs_index = 0; pacs_index < no_of_pacs; pacs_index++)
            {
                AudioContextType pacs_audiocontext = LeBapPacsUtilities_GetPreferredAudioContext(pacs_record[pacs_index].metadata, pacs_record[pacs_index].metadataLength);
        
                if ((audiocontext & pacs_audiocontext) && (sample_rate & pacs_record[pacs_index].supportedSamplingFrequencies))
                {
                    match_found = TRUE;
                    break;
                }
            }
        }
        break;
#if defined (INCLUDE_LE_APTX_ADAPTIVE) || defined (INCLUDE_LE_AUDIO_GAMING_MODE_APTX_LITE)
        case KYMERA_LE_AUDIO_CODEC_APTX_ADAPTIVE:
        case KYMERA_LE_AUDIO_CODEC_APTX_LITE:
        {
            const GattPacsServerVSPacRecord* pacs_record = LeBapPacsUtilities_GetPacsVS(is_source, &no_of_pacs, codec_type);
            PacsSamplingFrequencyType pacs_sample_rate;
    
            for (pacs_index = 0; pacs_index < no_of_pacs; pacs_index++)
            {
                AudioContextType pacs_audiocontext = LeBapPacsUtilities_GetPreferredAudioContext(pacs_record[pacs_index].metadata, pacs_record[pacs_index].metadataLength);
                pacs_sample_rate = LeBapPacsUtilities_GetSampleRateFromVSPac(&pacs_record[pacs_index]);
    
                if ((audiocontext & pacs_audiocontext) && (sample_rate & pacs_sample_rate))
                {
                    match_found = TRUE;
                    break;
                }
            }
        }
        break;
#endif
        default:
            break;
    }

    return match_found;
}

static bool leUnicastManager_ValidateSamplingRateForASE(gatt_cid_t cid, uint16 ase_id, uint16 audiocontext)
{
    bool sampling_rate_matched = FALSE;
    uint8 direction = LeBapUnicastServer_GetAseDirection(cid, ase_id);
    BapServerAseCodecInfo *codec_info = LeBapUnicastServer_GetCodecParameters(cid, ase_id);
    PacsSamplingFrequencyType sample_rate = codec_info == NULL ? 0 :
                    LeBapPacsUtilities_GetPacsSamplingFreqBitMaskFromFreq(LeUnicastManager_GetSampleRate(codec_info));
    appKymeraLeAudioCodec codec_type = codec_info == NULL ? KYMERA_LE_AUDIO_CODEC_LC3 :
                    leUnicastManager_GetCodecType(codec_info);

    switch (direction)
    {
        case GATT_ASCS_ASE_DIRECTION_SERVER_IS_AUDIO_SINK:
            sampling_rate_matched = leUnicastManager_SampleRateLookupOnPacs(FALSE, audiocontext, sample_rate, codec_type);
        break;

        case GATT_ASCS_ASE_DIRECTION_SERVER_IS_AUDIO_SOURCE:
            sampling_rate_matched =  leUnicastManager_SampleRateLookupOnPacs(TRUE, audiocontext, sample_rate, codec_type);
        break;

        default:
        break;
    }

    return sampling_rate_matched;
}

static AudioContextType leUnicastManager_DetermineAudioContext(gatt_cid_t cid, uint8 ase_count, BapServerEnableIndInfo *p_enable_info)
{
    AudioContextType audio_context;
    uint8 scan;

    audio_context = BapServerLtvUtilitiesGetStreamingAudioContext(p_enable_info[0].metadata, p_enable_info[0].metadataLength);

    if (audio_context == AUDIO_CONTEXT_TYPE_UNKNOWN || (audio_context & AUDIO_CONTEXT_TYPE_UNSPECIFIED))
    {
        uint8 src_ase_count = 0;
        uint8 snk_ase_count = 0;
        uint16 src_sample_rate = 0;
        uint16 snk_sample_rate = 0;
        uint16 sample_rate;

        for (scan = 0; scan < ase_count; scan++)
        {
            const BapServerAseCodecInfo *codec_info = LeBapUnicastServer_GetCodecParameters(cid, p_enable_info[scan].aseId);

            PanicNull((void *) codec_info);
            sample_rate = LeUnicastManager_GetSampleRate(codec_info);

            if ((LeBapUnicastServer_GetAseDirection(cid, p_enable_info[scan].aseId)) == ASE_DIRECTION_AUDIO_SINK)
            {
                snk_ase_count++;
                snk_sample_rate = sample_rate;
            }
            else
            {
                src_ase_count++;
                src_sample_rate = sample_rate;
            }
        }

        if (src_ase_count == 0)
        {
            audio_context = AUDIO_CONTEXT_TYPE_MEDIA;
        }
        else if (snk_ase_count == 0)
        {
            audio_context = AUDIO_CONTEXT_TYPE_LIVE;
        }
        else
        {
            audio_context = src_sample_rate == snk_sample_rate ? AUDIO_CONTEXT_TYPE_COVERSATIONAL : AUDIO_CONTEXT_TYPE_GAME;
        }
    }

    return audio_context;
}

/*!< Check if any change ase1 is routed and ase2 is not in ready to be routed state */
static bool leUnicastManager_IsAnyChangeInBetweenAseRouteState(le_um_ase_t *ase1, le_um_ase_t *ase2)
{
    bool reconfig = FALSE;

    if ((ase1->state == le_um_ase_state_routed || ase1->state == le_um_ase_state_streaming) &&
        LeUnicastManager_IsAseActive(ase2) &&
        ase2->cis_data != NULL &&
        LeUnicastManager_IsCisEstablished(ase2->cis_data->state))
    {
        reconfig = TRUE;
    }

    return reconfig;
}

static bool leUnicastManager_IsReconfigRequired(multidevice_side_t side)
{
    bool is_reconfig_needed = FALSE;
    le_um_ase_t *sink_ase = NULL;
    le_um_ase_t *source_ase = NULL;

    leUnicastManager_GetAseFromSide(side, &sink_ase, &source_ase);

    if(leUnicastManager_IsAnyChangeInBetweenAseRouteState(source_ase, sink_ase) || 
        leUnicastManager_IsAnyChangeInBetweenAseRouteState(sink_ase, source_ase))
    {
        is_reconfig_needed = TRUE;
    }

    DEBUG_LOG_INFO("leUnicastManager_IsReconfigRequired side: %d, is_reconfig_needed: %d", side, is_reconfig_needed);

    return is_reconfig_needed;
}

/*! \brief Check and move newly Enabled Sink ASEes to streaming state */
static bool leUnicastManager_CheckAndMoveAsesToStreamingState(void)
{
    bool found = FALSE;
    uint8 sink_ase_count = 0;
    le_um_ase_t *ase, *end_ase;
    le_um_ase_t *sink_ases[le_um_audio_location_max];

    for (ase = LeUnicastManager_GetAseListBase(), end_ase = LeUnicastManager_GetAseListEnd();
         ase <= end_ase;
         ase++)
    {
        if (ase->state == le_um_ase_state_enabling && ase->cis_data != NULL && ase->cis_data->state == le_um_cis_state_data_path_ready)
        {
            UNICAST_MANAGER_LOG("leUnicastManager_CheckAndMoveAsesToStreamingState: ASE ID %d", ase->ase_id);

            if (ase->direction == GATT_ASCS_ASE_DIRECTION_SERVER_IS_AUDIO_SINK)
            {
                sink_ases[sink_ase_count++] = ase;
            }

            found = TRUE;
        }
    }

    /* Move the newly enabled ASEs into streaming state whose CIS is already established */
    leUnicastManager_MoveAseToStreamingState(sink_ase_count, sink_ases);

    return found;
}

static void leUnicastManager_BapMessageHandler(Message message)
{
    BapServerPrim id;

    PanicNull((void*) message);

#ifdef USE_SYNERGY
    id = *(BapServerPrim *) message;
#else
    id = 0xFFFF;
#endif

    switch(id)
    {
        case BAP_SERVER_ASE_CODEC_CONFIGURED_IND:
        {
            BapServerAseCodecConfiguredInd *ind = (BapServerAseCodecConfiguredInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_CODEC_CONFIGURED_IND ase=0x%x direction: %d",
                           ind->aseCodecConfig.aseId, ind->aseCodecConfig.direction);
        }
        break;

        case BAP_SERVER_ASE_QOS_CONFIGURED_IND:
        {
            BapServerAseQosConfiguredInd *ind = (BapServerAseQosConfiguredInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_QOS_CONFIGURED_IND ase=0x%x cis: 0x%x",
                           ind->aseQosConfig.aseId, ind->aseQosConfig.cisId);
            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_QOS_CONFIGURED_IND sampleRate=%d sduSize: %d, frameDuration: %d, presentationDelay: %d",
                           ind->aseQosConfig.sampleRate, ind->aseQosConfig.sduSize, ind->aseQosConfig.frameDuration, ind->aseQosConfig.presentationDelay);
        }
        break;

        case BAP_SERVER_ASE_ENABLED_IND:
        {
            BapServerAseEnabledInd *ind = (BapServerAseEnabledInd *) message;
            BapServerAseResult *aseResult =  PanicUnlessMalloc(ind->numAses * sizeof(BapServerAseResult));
            AudioContextType audio_context;
            bool unicast_allowed;

            if (ind->connectionId && ind->numAses)
            {
                uint8 i;
                GattAscsAseResultValue result;

#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)
                unicast_allowed = LeUnicastManager_GetContext()->feature_context.saved_sink_available_context == 0 &&
                                  LeUnicastManager_GetContext()->feature_context.saved_source_available_context == 0;
#else
                unicast_allowed = TRUE;
#endif

                audio_context = leUnicastManager_DetermineAudioContext(ind->connectionId, ind->numAses, ind->bapServerEnableIndInfo);
                result = unicast_allowed ? GATT_ASCS_ASE_RESULT_SUCCESS : GATT_ASCS_ASE_RESULT_INSUFFICIENT_RESOURCES;

                for (i = 0; i < ind->numAses; i++)
                {
                    aseResult[i].aseId = ind->bapServerEnableIndInfo[i].aseId;
                    aseResult[i].additionalInfo = 0;

                    if (result == GATT_ASCS_ASE_RESULT_SUCCESS)
                    {
                        result = BapServerValidateMetadataLtvs(ind->bapServerEnableIndInfo[i].metadata,
                                                                           ind->bapServerEnableIndInfo[i].metadataLength,
                                                                           &aseResult[i].additionalInfo);
                    }

                    if (result == GATT_ASCS_ASE_RESULT_SUCCESS)
                    {
                        if (BapServerValidateStreamingContext(bapUnicastServiceHandle, ind->connectionId,
                                                              ind->bapServerEnableIndInfo[i].aseId,
                                                              ind->bapServerEnableIndInfo[i].metadata,
                                                              &ind->bapServerEnableIndInfo[i].metadataLength) &&
                            leUnicastManager_ValidateSamplingRateForASE(ind->connectionId,
                                                                        ind->bapServerEnableIndInfo[i].aseId,
                                                                        audio_context))
                        {
                            AseMetadataType metadata;

                            metadata.aseId = ind->bapServerEnableIndInfo[i].aseId;
                            metadata.metadataLength = ind->bapServerEnableIndInfo[i].metadataLength;
                            metadata.metadata = ind->bapServerEnableIndInfo[i].metadata;

                            if (!leUnicastManager_AseEnabled(ind->connectionId, &metadata, audio_context))
                            {
                                result = GATT_ASCS_ASE_RESULT_UNSPECIFIED_ERROR;
                                aseResult[i].additionalInfo = 0;
                            }
                        }
                        else
                        {
                            result = GATT_ASCS_ASE_RESULT_REJECTED_METADATA;
                            aseResult[i].additionalInfo = BAP_METADATA_LTV_TYPE_STREAMING_AUDIO_CONTEXTS;
                        }
                    }

                    aseResult[i].value = result;
                    DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_ENABLED_IND ase=0x%x cis: 0x%x, audio_context: 0x%x, result=0x%x, additional_info: 0x%x",
                                   ind->bapServerEnableIndInfo[i].aseId, ind->bapServerEnableIndInfo[i].cisId, audio_context, aseResult[i].value, aseResult[i].additionalInfo);
                }

                BapServerUnicastAseEnableRsp(bapUnicastServiceHandle, ind->connectionId, ind->numAses, aseResult);

                if (result == GATT_ASCS_ASE_RESULT_SUCCESS)
                {
                    bool reconfig_our_side = FALSE;
                    
                    /* Check and move newly enabled ASEes to Streaming state */
                    leUnicastManager_CheckAndMoveAsesToStreamingState();
                    
                    /* If all CISes were established earlier, then data path for them */
                    leUnicastManager_CheckAndEstablishDataPath(TRUE);
                    /* Check if both CIS and data path are ready for this ASE earlier along with previoulsy enabled ASE */
                    reconfig_our_side = leUnicastManager_IsReconfigRequired(Multidevice_GetSide());
                    if (reconfig_our_side)
                    {
                        /* Need to reconfigure the graph as new ASEs in CIS are enabled here */
                       LeUnicastMusicSource_Reconfig();
                    }
                }
            }

            pfree(aseResult);
        }
        break;

        case BAP_SERVER_CIS_ESTABLISHED_IND:
        {
            BapServerCisEstablishedInd *ind = (BapServerCisEstablishedInd *) message;
            uint8 dir;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler BAP_SERVER_CIS_ESTABLISHED_IND cis: 0x%x, handle: 0x%x",
                           ind->cisId, ind->cisHandle);

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler CIS Params cigSyncDelay: 0x%x, cisSyncDelay: 0x%x, isoInt: 0x%x, nse: 0x%x",
                           ind->cisParams.cigSyncDelay, ind->cisParams.cisSyncDelay, ind->cisParams.isoInterval, ind->cisParams.nse);
            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler CIS Params MtoS transLat: 0x%x, maxPDU: 0x%x, phy: 0x%x, bn: 0x%x, ft: 0x%x",
                           ind->cisParams.transportLatencyMtoS, ind->cisParams.maxPduMtoS, ind->cisParams.phyMtoS, ind->cisParams.bnMtoS,
                           ind->cisParams.ftMtoS);
            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler CIS Params StoM transLat: 0x%x, maxPDU: 0x%x, phy: 0x%x, bn: 0x%x, ft: 0x%x",
                           ind->cisParams.transportLatencyStoM, ind->cisParams.maxPduStoM, ind->cisParams.phyStoM, ind->cisParams.bnStoM,
                           ind->cisParams.ftStoM);

            dir = ind->cisParams.bnMtoS != 0 ? LE_UM_CIS_DIRECTION_DL : 0;
            dir |= ind->cisParams.bnStoM != 0 ? LE_UM_CIS_DIRECTION_UL : 0;
            leUnicastManager_CisEstablished(ind->cisId, ind->cisHandle, dir);
        }
        break;

        case BAP_SERVER_SETUP_DATA_PATH_CFM:
        {
            BapServerSetupDataPathCfm *cfm = (BapServerSetupDataPathCfm *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler BAP_SERVER_SETUP_DATA_PATH_CFM handle: 0x%x, status: 0x%x",
                           cfm->isoHandle, cfm->status);
            if (cfm->status == BAP_SERVER_STATUS_SUCCESS)
            {
                leUnicastManager_DataPathCreated(cfm->isoHandle);
            }
        }
        break;

        case BAP_SERVER_CIS_DISCONNECTED_IND:
        {
            BapServerCisDisconnectedInd *ind = (BapServerCisDisconnectedInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler BAP_SERVER_CIS_DISCONNECTED_IND CIS handle: 0x%x, reason: 0x%x",
                           ind->cisHandle, ind->reason);

            leUnicastManager_CisDisconnected(ind->cisHandle);
        }
        break;

        case BAP_SERVER_CIS_DISCONNECTED_CFM:
        {
            BapServerCisDisconnectedCfm *cfm = (BapServerCisDisconnectedCfm *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler BAP_SERVER_CIS_DISCONNECTED_CFM CIS handle: 0x%x, reason: 0x%x",
                           cfm->cisHandle, cfm->status);
            leUnicastManager_CisDisconnected(cfm->cisHandle);
        }
        break;

        case BAP_SERVER_ASE_RECEIVER_START_READY_IND:
        {
            BapServerAseReceiverStartReadyInd *ind = (BapServerAseReceiverStartReadyInd *)message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_RECEIVER_START_READY_IND ase=0x%x",
                           ind->aseId);
            leUnicastManager_AseReceiverReady(ind->connectionId, ind->aseId);
        }
        break;

        case BAP_SERVER_ASE_UPDATE_METADATA_IND:
        {
            BapServerAseUpdateMetadataInd *ind = (BapServerAseUpdateMetadataInd *) message;

            leUnicastManager_UpdateMetadata(ind->connectionId, &ind->aseMetadata);
        }
        break;

        case BAP_SERVER_ASE_DISABLED_IND:
        {
            BapServerAseDisabledInd *ind = (BapServerAseDisabledInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_DISABLED_IND ase=0x%x",
                           ind->aseId);
            leUnicastManager_AseDisabled(ind->connectionId, ind->aseId);
        }
        break;

        case BAP_SERVER_ASE_RECEIVER_STOP_READY_IND:
        {
            BapServerAseReceiverStopReadyInd *ind = (BapServerAseReceiverStopReadyInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_RECEIVER_STOP_READY_IND ase=0x%x",
                           ind->aseId);
            leUnicastManager_AseReceiverStop(ind->connectionId, ind->aseId);
        }
        break;

        case BAP_SERVER_ASE_RELEASED_IND:
        {
            BapServerAseReleasedInd *ind = (BapServerAseReleasedInd *) message;

            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler : BAP_SERVER_ASE_RELEASED_IND ase=0x%x", ind->aseId);
            leUnicastManager_AseReleased(ind->connectionId, ind->aseId);
        }
        break;

        case BAP_SERVER_REMOVE_DATA_PATH_CFM:
        {
            DEBUG_LOG_INFO("leUnicastManager_BapMessageHandler BAP_SERVER_REMOVE_DATA_PATH_CFM");
        }
        break;

        default:
            Panic();
        break;
    }
}

#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)

static void leUnicastManager_MsgHandlerOtherAudioSrcMessages(MessageId id, Message message)
{

    device_t device_bredr;
    device_t device_le;
    bool allow_le_audio;

    if (id == TELEPHONY_AUDIO_CONNECTED || id == TELEPHONY_AUDIO_DISCONNECTED || id == TELEPHONY_CALL_ENDED ||
        id == TELEPHONY_INCOMING_CALL_ENDED || id == TELEPHONY_OUTGOING_CALL_ENDED)
    {
        const telephony_message_t *telephony_msg = (const telephony_message_t *) message;

        device_bredr = DeviceList_GetFirstDeviceWithPropertyValue(device_property_voice_source, &telephony_msg->voice_source, sizeof(voice_source_t));
        allow_le_audio = id != TELEPHONY_AUDIO_CONNECTED;
    }
    else if (id == AV_STREAMING_ACTIVE_IND || id == AV_STREAMING_INACTIVE_IND)
    {
        audio_source_t audio_source;

        if (id == AV_STREAMING_ACTIVE_IND)
        {
            allow_le_audio = FALSE;
            audio_source = AudioSources_GetRoutedSource();
        }
        else
        {
            allow_le_audio = TRUE;
            audio_source = AudioRouter_GetLastRoutedAudio();
        }

        device_bredr = DeviceList_GetFirstDeviceWithPropertyValue(device_property_audio_source, &audio_source, sizeof(audio_source_t));
    }
    else
    {
        return;
    }

    device_le = GattConnect_GetBtDevice(LeUnicastManager_GetContext()->feature_context.connected_cid);

    if (device_bredr == NULL || device_bredr == device_le)
    {
        DEBUG_LOG_INFO("leUnicastManager_MsgHandlerOtherAudioSrcMessages Same device");
        return;
    }

    LeUnicastManager_UpdateFeatureAllowedStatus(allow_le_audio);
}

#endif /* ENABLE_MULTIPOINT && ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT */

static void leUnicastManager_HandleMuteInd(MICP_SERVER_MUTE_IND_T *ind)
{
    uint8 mute_state = ind->mute_state;
    AudioContextType audio_context = LeUnicastManager_GetContext()->audio_context;
    voice_source_t source = voice_source_le_audio_unicast;
    bool mic_active = FALSE;

    if (!LeUnicastManager_IsStreamingActive())
    {
        return;
    }

    if (LeUnicastManager_IsContextTypeConversational(audio_context))
    {
        if (VoiceSources_IsAudioRouted(source))
        {
            /* Notify Telephony mute state is active /inactive 
            TODO : Expand this message as a generic for Gaming Mode + VBC and SREC use case */
            if (mute_state)
            {
                Telephony_NotifyMicrophoneMuted(source);
            }
            else
            {
                Telephony_NotifyMicrophoneUnmuted(source);
            }

            mic_active = TRUE;
        }
    }
    else if (LeUnicastManager_IsSourceAseActive())
    {
        mic_active = TRUE;
    }

    DEBUG_LOG("leUnicastManager_HandleMuteInd mute-state:=%d, mic-active: %d", ind->mute_state, mic_active);
    if (mic_active)
    {
        KymeraLeAudioVoice_SetMicMuteState(mute_state);

#ifdef ENABLE_LEA_CIS_DELEGATION
        MirrorProfile_SendMicMute(mute_state);
#endif
    }
}

static void leUnicastManager_MessageHandler(Task task, MessageId id, Message message)
{
    UNUSED(task);

    switch (id)
    {
        case BAP_SRVR_PRIM:
            leUnicastManager_BapMessageHandler(message);
            break;

        case MICP_SERVER_MUTE_IND:
            leUnicastManager_HandleMuteInd((MICP_SERVER_MUTE_IND_T *)message);
            break;

        default:
#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)
            if (ID_IN_MSG_GRP(AV, id) || ID_IN_MSG_GRP(TELEPHONY, id))
            {
                leUnicastManager_MsgHandlerOtherAudioSrcMessages(id, message);
            }
#endif
            break;
    }
}

static void leUnicastManager_OnGattConnect(gatt_cid_t cid)
{
    UNUSED(cid);

    TimestampEvent(TIMESTAMP_EVENT_LE_UNICAST_ACL_CONNECT);
}

static void leUnicastManager_OnGattDisconnect(gatt_cid_t cid)
{
    BapAscsConfig *config = BapServerUnicastRemoveAscsConfig(bapUnicastServiceHandle, cid);

    UNICAST_MANAGER_LOG("leUnicastManager_OnGattDisconnect cid=0x%x", cid);

    if(config)
    {
#ifdef USE_SYNERGY
        uint8 size = offsetof(BapAscsConfig, aseCharClientCfg) + config->numAses * sizeof(ClientConfig);
        uint8 *data = (uint8 *) PanicUnlessMalloc(size);

        memcpy(data, &config->numAses, sizeof(config->numAses));
        memcpy(data + offsetof(BapAscsConfig, aseControlPointCharClientCfg),
               &config->aseControlPointCharClientCfg,
               sizeof(config->aseControlPointCharClientCfg));
        memcpy(data + offsetof(BapAscsConfig, aseCharClientCfg),
               config->aseCharClientCfg,
               config->numAses * sizeof(ClientConfig));

        leUnicastManager_StoreClientConfig(cid, data, size);
        pfree(data);

        pfree(config->aseCharClientCfg);
#endif
        pfree(config);
    }

    leUnicastManager_CidDisconnected(cid);
#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)
    if (LeUnicastManager_GetContext()->feature_context.connected_cid == cid)
    {
        LeUnicastManager_GetContext()->feature_context.connected_cid = INVALID_CID;
    }
#endif
}

static void leUnicastManager_OnEncryptionChanged(gatt_cid_t cid, bool encrypted)
{
    if (encrypted && !GattConnect_IsDeviceTypeOfPeer(cid))
    {
        uint8 *data = NULL;
        BapAscsConfig config = {0};
        bapStatus status;

#ifdef USE_SYNERGY
        data = (uint8 *)leUnicastManager_RetrieveClientConfig(cid);

        if (data != NULL)
        {
            memcpy(&config.numAses, data, sizeof(config.numAses));
            memcpy(&config.aseControlPointCharClientCfg,
                   data + leUnicastManager_GetCCCDOffset(),
                   sizeof(config.aseControlPointCharClientCfg));
            config.aseCharClientCfg = (ClientConfig*) &data[leUnicastManager_GetClientCfgOffset()];
        }
#else
        (void) config;
#endif
        UNICAST_MANAGER_LOG("leUnicastManager_OnEncryptionChanged add ASCS config handle=0x%p cid=0x%x config=0x%x",
                            bapUnicastServiceHandle, cid, data);
        status = BapServerUnicastAddAscsConfig(bapUnicastServiceHandle, cid, data != NULL ? &config : NULL);
        if (status != BAP_SERVER_STATUS_SUCCESS)
        {
            UNICAST_MANAGER_LOG("leUnicastManager_OnEncryptionChanged add ASCS config failed status=%d", status);
            Panic();
        }
        else
        {
#if defined(ENABLE_MULTIPOINT) && defined(ENABLE_LE_AUDIO_RESTRICTED_MULTIPOINT)
            LeUnicastManager_GetContext()->feature_context.connected_cid = cid;
#endif
        }
    }
}

void LeUnicastManagerTask_Init(void)
{
    LeUnicastManager_GetTask()->handler = leUnicastManager_MessageHandler;
    LeBapUnicastServer_Init(NUMBER_OF_ASES_REQUIRED, LeUnicastManager_GetTask());
#ifdef ENABLE_TMAP_PROFILE
    TmapProfile_Init();
#endif
    MicpServer_Init();
    MicpServer_ClientRegister(LeUnicastManager_GetTask());
    GattConnect_RegisterObserver(&leUnicastManager_connect_callbacks);
    GattConnect_UpdateMinAcceptableMtu(leUnicastManagerConfig_GattMtuMinimum());
}

#endif /* defined(INCLUDE_LE_AUDIO_UNICAST) */
